package com.luoxiang.socialandroid.activity

import android.annotation.SuppressLint
import android.content.ContentValues
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.view.MotionEvent
import android.view.View
import android.widget.Toast
import androidx.camera.core.CameraSelector
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.video.*
import androidx.core.content.ContextCompat
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.lifecycleScope
import cn.jzvd.Jzvd
import com.alibaba.sdk.android.oss.ClientConfiguration
import com.alibaba.sdk.android.oss.ClientException
import com.alibaba.sdk.android.oss.OSSClient
import com.alibaba.sdk.android.oss.ServiceException
import com.alibaba.sdk.android.oss.callback.OSSCompletedCallback
import com.alibaba.sdk.android.oss.common.auth.OSSCredentialProvider
import com.alibaba.sdk.android.oss.common.auth.OSSStsTokenCredentialProvider
import com.alibaba.sdk.android.oss.model.PutObjectRequest
import com.alibaba.sdk.android.oss.model.PutObjectResult
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.ToastUtils
import com.google.common.util.concurrent.ListenableFuture
import com.google.gson.Gson
import com.luoxiang.socialandroid.R
import com.luoxiang.socialandroid.base.BaseActivity
import com.luoxiang.socialandroid.bean.NumberBean
import com.luoxiang.socialandroid.bean.TokenBean
import com.luoxiang.socialandroid.common.Constants.MmkvConst.KEY
import com.luoxiang.socialandroid.databinding.ActivityCameraVideoBinding
import com.luoxiang.socialandroid.extensions.ifNull
import com.luoxiang.socialandroid.utils.AESUtil
import com.luoxiang.socialandroid.utils.NetUtils2
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.io.File
import java.text.SimpleDateFormat
import java.util.*


class CameraVideoActivity : BaseActivity() {
    companion object {
        private const val FILENAME_FORMAT = "yyyy-mm-dd HH:mm:ss"
    }

    enum class CameraVideoStatus {
        /***
         * 未开始录制*/
        NOT_RECORDED,

        /***
         * 录制中*/
        IN_THE_RECORDING,

        /***
         * 录制成功*/
        RECORDING_SUCCESS,
    }

    private lateinit var mViewBinding: ActivityCameraVideoBinding
    private lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>
    private var videoCapture: VideoCapture<Recorder>? = null
    private var pendingRecording: PendingRecording? = null
    private var recording: Recording? = null
    private var mediaStoreOutput: MediaStoreOutputOptions? = null
    private var minDuration: Long = 3 * 1000
    private var maxDuration: Long = 10 * 1000
    private var minRecordingCountDownJob: Job? = null
    private var maxRecordingCountDownJob: Job? = null
    private var recordingProgressJob: Job? = null
    private val cameraVideoStatusResult = MutableLiveData(CameraVideoStatus.NOT_RECORDED)
    private val recordingProgressResult = MutableLiveData(0)
    private var outputUri: Uri? = null
    private var cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
    private var cameraUnbindBlock: () -> Unit = {}
    private var videoPath: String? = null
    private var mIsSubmitStats: Boolean = false

    //是否允许停止录制
    private var isAllowStop = false

    override fun initLayout(savedInstanceState: Bundle?) {
        mViewBinding = ActivityCameraVideoBinding.inflate(layoutInflater)
        setContentView(mViewBinding.root)
        setStatusBarTranslucent()
    }

    override fun initView() {
        cameraProviderFuture = ProcessCameraProvider.getInstance(this)
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun initListener() {
        mViewBinding.imageBack.setOnClickListener {
            finish()
        }
        mViewBinding.imageCameraFlip.setOnClickListener {
            cameraUnbindBlock.invoke()
            val cameraProvider = cameraProviderFuture.get()
            bindPreview(cameraProvider)
        }
        cameraProviderFuture.addListener({
            val cameraProvider = cameraProviderFuture.get()
            bindPreview(cameraProvider)
        }, ContextCompat.getMainExecutor(this))
        mViewBinding.imageStart.setOnTouchListener { _, event ->
            if (event.action == MotionEvent.ACTION_DOWN) {
                recording = pendingRecording?.start(ContextCompat.getMainExecutor(this)) {
                    if (it is VideoRecordEvent.Finalize) {
                        if (cameraVideoStatusResult.value == CameraVideoStatus.NOT_RECORDED || cameraVideoStatusResult.value == CameraVideoStatus.IN_THE_RECORDING) {
                            deleteVideo(it.outputResults.outputUri)
                            return@start
                        }
                        this.outputUri = it.outputResults.outputUri
                        playVideo()
                    }
                }
                cameraVideoStatusResult.value = CameraVideoStatus.IN_THE_RECORDING
            } else if (event.action == MotionEvent.ACTION_UP || event.action == MotionEvent.ACTION_CANCEL) {
                if (isAllowStop) {
                    closeAndSaveVideoCapture()
                } else {
                    if (cameraVideoStatusResult.value != CameraVideoStatus.IN_THE_RECORDING) {
                        return@setOnTouchListener true
                    }
                    Toast.makeText(this, getString(R.string.mi_luzhishibai), Toast.LENGTH_SHORT)
                        .show()
                    closeVideoCapture()
                }
            }
            return@setOnTouchListener true
        }
        mViewBinding.imageReRecording.setOnClickListener {
            closeVideoCapture()
            deleteVideo(outputUri)
        }
        mViewBinding.imageNextStep.setOnClickListener {
            submit()
        }
        cameraVideoStatusResult.observe(this) {
            changeUIForCameraVideoStatus(it)
            when (it) {
                CameraVideoStatus.NOT_RECORDED -> {
                    recordingProgressJob?.cancel()
                    minRecordingCountDownJob?.cancel()
                    maxRecordingCountDownJob?.cancel()
                    isAllowStop = false
                }

                CameraVideoStatus.IN_THE_RECORDING -> {
                    recordingProgressJob = lifecycleScope.launch {
                        recordingProgressResult.value = 0
                        val interval = 100L
                        while (true) {
                            if ((recordingProgressResult.value ?: 0) >= maxDuration) {
                                cancel()
                                return@launch
                            }
                            delay(interval)
                            recordingProgressResult.value =
                                (recordingProgressResult.value ?: 0) + interval.toInt()
                        }
                    }
                    minRecordingCountDownJob = lifecycleScope.launch {
                        delay(minDuration)
                        isAllowStop = true
                    }
                    maxRecordingCountDownJob = lifecycleScope.launch {
                        delay(maxDuration)
                        closeAndSaveVideoCapture()
                    }
                }

                CameraVideoStatus.RECORDING_SUCCESS -> {
                    recordingProgressJob?.cancel()
                    minRecordingCountDownJob?.cancel()
                    maxRecordingCountDownJob?.cancel()
                }

                else -> {}
            }
        }
        recordingProgressResult.observe(this) {
            mViewBinding.cropProgressBar.setProgress((it / 100).toFloat())
        }
    }

    /***
     * 根据录制状态展示UI*/
    private fun changeUIForCameraVideoStatus(status: CameraVideoStatus) {
        when (status) {
            CameraVideoStatus.NOT_RECORDED -> {
                mViewBinding.imageReRecording.visibility = View.GONE
                mViewBinding.textReRecording.visibility = View.GONE
                mViewBinding.imageNextStep.visibility = View.GONE
                mViewBinding.textNextStep.visibility = View.GONE
                mViewBinding.cropProgressBar.visibility = View.GONE
                mViewBinding.imageStart.visibility = View.VISIBLE
                mViewBinding.textRecordingTitle.visibility = View.VISIBLE
                mViewBinding.imageCameraFlip.visibility = View.VISIBLE
                mViewBinding.imageStart.setImageDrawable(
                    ContextCompat.getDrawable(
                        this,
                        com.luoxiang.socialandroid.R.mipmap.icon_recording
                    )
                )
                mViewBinding.textRecordingTitle.text = getString(R.string.mi_changanpaishe)
                closeVideo()
            }

            CameraVideoStatus.IN_THE_RECORDING -> {
                mViewBinding.imageReRecording.visibility = View.GONE
                mViewBinding.textReRecording.visibility = View.GONE
                mViewBinding.imageNextStep.visibility = View.GONE
                mViewBinding.textNextStep.visibility = View.GONE
                mViewBinding.cropProgressBar.visibility = View.VISIBLE
                mViewBinding.imageStart.visibility = View.VISIBLE
                mViewBinding.textRecordingTitle.visibility = View.VISIBLE
                mViewBinding.imageCameraFlip.visibility = View.GONE
                mViewBinding.imageStart.setImageDrawable(
                    ContextCompat.getDrawable(
                        this,
                        R.mipmap.icon_in_the_recording
                    )
                )
                mViewBinding.textRecordingTitle.text = getString(R.string.mi_luzhizhong)
                closeVideo()
            }

            CameraVideoStatus.RECORDING_SUCCESS -> {
                mViewBinding.imageReRecording.visibility = View.VISIBLE
                mViewBinding.textReRecording.visibility = View.VISIBLE
                mViewBinding.imageNextStep.visibility = View.VISIBLE
                mViewBinding.textNextStep.visibility = View.VISIBLE
                mViewBinding.cropProgressBar.visibility = View.GONE
                mViewBinding.imageStart.visibility = View.GONE
                mViewBinding.textRecordingTitle.visibility = View.GONE
                mViewBinding.imageCameraFlip.visibility = View.GONE
            }
        }
    }

    private fun bindPreview(cameraProvider: ProcessCameraProvider) {
        val preview = Preview.Builder().build()
        preview.setSurfaceProvider(mViewBinding.previewView.surfaceProvider)
        val qualitySelector = QualitySelector.fromOrderedList(
            listOf(Quality.UHD, Quality.FHD, Quality.HD, Quality.SD),
            FallbackStrategy.lowerQualityOrHigherThan(Quality.SD)
        )
        val recorder = Recorder.Builder()
            .setExecutor(ContextCompat.getMainExecutor(this)).setQualitySelector(qualitySelector)
            .build()
        videoCapture = VideoCapture.withOutput(recorder)
        //获取相机实例，需要调整相机参数请使用该对象
        val camera = cameraProvider.bindToLifecycle(
            this, if (cameraSelector == CameraSelector.DEFAULT_FRONT_CAMERA) {
                cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
                cameraSelector
            } else {
                cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA
                cameraSelector
            }, preview, videoCapture
        )
        val name = "authentication-social-" +
                SimpleDateFormat(FILENAME_FORMAT, Locale.CHINA)
                    .format(System.currentTimeMillis()) + ".mp4"
        val contentValues = ContentValues().apply {
            put(MediaStore.Video.Media.DISPLAY_NAME, name)
        }
        mediaStoreOutput = MediaStoreOutputOptions.Builder(
            this.contentResolver,
            MediaStore.Video.Media.EXTERNAL_CONTENT_URI
        )
            .setContentValues(contentValues)
            .build()
        pendingRecording = videoCapture?.output
            ?.prepareRecording(this, mediaStoreOutput!!)
        cameraUnbindBlock = {
            cameraProvider.unbindAll()
        }
    }


    private fun playVideo() {
        mViewBinding.jzVideoPlayer.visibility = View.VISIBLE
        val videoPath = videoUriToRealPath(outputUri).ifNull {
            Toast.makeText(this, getString(R.string.mi_weihuoqudaoluzhixinxi), Toast.LENGTH_SHORT)
                .show()
            return
        }
        mViewBinding.jzVideoPlayer.isLoop = true
        mViewBinding.jzVideoPlayer.setUp(videoPath, "")
        mViewBinding.jzVideoPlayer.startVideo()
    }

    private fun closeVideo() {
        mViewBinding.jzVideoPlayer.visibility = View.GONE
        mViewBinding.jzVideoPlayer.isLoop = false
        mViewBinding.jzVideoPlayer.onStateAutoComplete()
    }

    private fun closeAndSaveVideoCapture() {
        recording?.stop()
        isAllowStop = false
        cameraVideoStatusResult.value = CameraVideoStatus.RECORDING_SUCCESS
    }

    private fun closeVideoCapture() {
        cameraVideoStatusResult.value = CameraVideoStatus.NOT_RECORDED
        recording?.stop()
    }

    /**
     * 获取OSS凭证
     */
    private fun getOssToken() {
        showLoading()
        NetUtils2.getInstance()
            .apiService
            .uploadToken
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : Observer<NumberBean> {
                override fun onSubscribe(d: Disposable?) {}

                override fun onComplete() {}

                override fun onError(e: Throwable?) {
                    ToastUtils.showShort(e?.message)
                    clearLoading()
                }

                override fun onNext(value: NumberBean?) {
                    if (value == null) {
                        ToastUtils.showShort("Oss凭证获取失败")
                        return
                    }
                    val json = AESUtil.aesDecryptString(value.data, KEY)
                    val token = Gson().fromJson(json, TokenBean::class.java)
                    token?.let {
                        LogUtils.d("OSS返回数据为:${it.data.toString()}")
                        ossSubmit(it)
                    }
                }
            })
    }

    /**
     * 上传OSS
     */
    private fun ossSubmit(token: TokenBean) {
        if (videoPath.isNullOrEmpty()) {
            ToastUtils.showShort("视频获取失败，请重新拍摄")
            return
        }
        val key = getkey(videoPath)
        val credentialProvider: OSSCredentialProvider = OSSStsTokenCredentialProvider(
            token.data.sts.accessKeyId,
            token.data.sts.accessKeySecret,
            token.data.sts.securityToken
        )
        val config = ClientConfiguration().apply {
            connectionTimeout = 15 * 1000 // 连接超时，默认15秒。
            socketTimeout = 15 * 1000 // socket超时，默认15秒。
            maxConcurrentRequest = 5 // 最大并发请求书，默认5个。
            maxErrorRetry = 2 // 失败后最大重试次数，默认2次。
        }
        val client = OSSClient(this, token.data.sts.endpoint, credentialProvider, config)
        client.asyncPutObject(
            PutObjectRequest(
                token.data.sts.bucketName,
                key,
                videoPath
            ), object : OSSCompletedCallback<PutObjectRequest, PutObjectResult> {
                override fun onSuccess(request: PutObjectRequest?, result: PutObjectResult?) {
                    clearLoading()
                    mIsSubmitStats = true
                    lifecycleScope.launch {
                        Toast.makeText(
                            this@CameraVideoActivity,
                            "认证视频上传成功",
                            Toast.LENGTH_SHORT
                        ).show()
                    }
                    startActivity(
                        Intent(
                            this@CameraVideoActivity,
                            UserInfoActivity::class.java
                        ).apply {
                            putExtra("leixing", 0)
                            putExtra("video", videoPath)
                            putExtra("videoPath", key)
                        })
                    finish()
                }

                override fun onFailure(
                    request: PutObjectRequest?,
                    clientException: ClientException?,
                    serviceException: ServiceException?
                ) {
                    clearLoading()
                    mIsSubmitStats = false
                    lifecycleScope.launch {
                        Toast.makeText(
                            this@CameraVideoActivity,
                            "认证视频上传失败，请重试:${clientException?.message}",
                            Toast.LENGTH_SHORT
                        ).show()
                    }
                }
            }).result
    }

    /***
     * 删除图片*/
    private fun deleteVideo(outputUri: Uri?) {
        val videoPath = videoUriToRealPath(outputUri) ?: return
        File(videoPath).delete()
    }

    /***
     * 提交*/
    @SuppressLint("Recycle")
    private fun submit() {
        videoPath = videoUriToRealPath(outputUri).ifNull {
            Toast.makeText(this, getString(R.string.mi_weihuoqudaoluzhixinxi), Toast.LENGTH_SHORT)
                .show()
            return
        }
        getOssToken()
    }

    @SuppressLint("Recycle")
    private fun videoUriToRealPath(outputUri: Uri?): String? {
        val cursor = outputUri.ifNull {
            return null
        }.let {
            this.contentResolver.query(
                it,
                arrayOf(MediaStore.Video.Media.DATA),
                null,
                null,
                null
            ).ifNull {
                return null
            }
        }
        if (cursor.moveToFirst()) {
            val columnIndex = cursor.getColumnIndex(MediaStore.Video.Media.DATA)
            if (columnIndex == -1) {
                return null
            }
            return cursor.getString(columnIndex)
        }
        return null
    }

    override fun onBackPressed() {
        if (Jzvd.backPress()) {
            return
        }
        super.onBackPressed()
    }

    override fun onPause() {
        super.onPause()
        Jzvd.releaseAllVideos()
    }
}